home *** CD-ROM | disk | FTP | other *** search
- RCS_ID_C "$Id: print.c,v 1.12 1993/11/12 06:03:26 ppessi Exp $";
- /*
- * print.c --- print a directory listing
- *
- * Author: ppessi <Pekka.Pessi@hut.fi>
- *
- * Copyright (c) 1991, 1993 Pekka Pessi. All rights reserved
- *
- * Last modified: Fri Nov 12 08:03:02 1993 ppessi
- *
- */
-
- #include <stdlib.h>
- #include <string.h>
- #include <dos/dosasl.h>
- #include <dos/dosextens.h>
- #include <dos/datetime.h>
- #include <clib/utility_protos.h>
-
- #include <exec/io.h>
- #include <exec/memory.h>
- #include <devices/conunit.h>
-
- #ifdef HAVE_MULTIUSER
- #include <libraries/multiuser.h>
- #else
- /* Only thing we need to know is SUID bit */
- #define muFIBB_SET_UID (31) /* Change owner during execution */
- #define muFIBF_SET_UID (1<<muFIBB_SET_UID)
- #endif
-
- #include "ls.h"
-
- #define FUDGE 2 /* space between columns */
- #define DEFAULTWIDTH 77 /* -C width */
-
- #ifndef FIBB_OTR_READ
- /*
- * These bits are undefined before DOS 3.0
- * FIBB are bit definitions, FIBF are field definitions
- * Regular RWED bits are 0 == allowed.
- * NOTE: GRP and OTR RWED permissions are 0 == not allowed!
- */
- #define FIBB_OTR_READ 15 /* Other: file is readable */
- #define FIBB_OTR_WRITE 14 /* Other: file is writable */
- #define FIBB_OTR_EXECUTE 13 /* Other: file is executable */
- #define FIBB_OTR_DELETE 12 /* Other: prevent file from being deleted */
- #define FIBB_GRP_READ 11 /* Group: file is readable */
- #define FIBB_GRP_WRITE 10 /* Group: file is writable */
- #define FIBB_GRP_EXECUTE 9 /* Group: file is executable */
- #define FIBB_GRP_DELETE 8 /* Group: prevent file from being deleted */
- #endif
-
- /*
- * checkbreak
- * exit if CTRL-C signal received
- */
- static void
- checkbreak(void)
- {
- if (CheckSignal(SIGBREAKF_CTRL_C)) {
- FPuts(Stderr, "***Break\n");
- exit(RETURN_FAIL);
- }
- }
-
- /*
- * getconwidth
- * Try to find out the width of console window
- */
- static int
- getconwidth(BPTR out)
- {
- int width;
- struct InfoData *id;
- struct MsgPort *conTask;
-
- if (!out || !IsInteractive(out)) {
- return 0;
- }
- conTask = ((struct FileHandle *)BADDR(out)) -> fh_Type;
- if ((long) conTask < 0)
- return 0;
- id = (struct InfoData *) AllocMem(sizeof(struct InfoData), MEMF_CLEAR);
- if (!id)
- return 0;
-
- if (DoPkt(conTask, ACTION_DISK_INFO, MKBADDR(id), NULL, NULL, NULL, NULL)) {
- #if 0
- struct ConUnit * conu = (id->id_InUse);
- FPrintf(err, "Window id: %lx\n", (long) (id->id_VolumeNode));
- FPrintf(err, "Console Unit: %lx\n", (long) (id->id_InUse));
- FPrintf(err, "Window Size: X %ld Y %ld \n", conu->cu_XMax, conu->cu_YMax);
- #endif
- width = ((struct ConUnit *)
- ( ((struct IOStdReq *)(id->id_InUse))->io_Unit ))->cu_XMax;
- } else {
- width = 0;
- }
- FreeMem(id, sizeof(*id));
- return width;
- }
-
- /*
- * count_rows
- * calculate minimum amount of row the short listing fits
- */
- static int
- count_rows(const UBYTE *widths,
- UBYTE * col_widths,
- int entries,
- int width,
- int fudge) /* fudge factor */
- {
- int col, row, rows, row_width;
- UBYTE * col_width;
-
- for (rows = 1; rows < entries; rows ++) {
- row_width = 0;
- col_width = col_widths;
- for (col = 0; col < entries; col += rows, row_width += fudge) {
- int w = 0;
- for (row = 0; row < rows && col + row < entries; row ++)
- w = max(w, widths[col + row]);
- row_width += w;
- *col_width++ = w;
- if (w > width)
- return entries;
- if (row_width > width)
- break;
- }
- if (row_width <= width)
- break;
- }
- return rows;
- }
-
- /*
- * type_char
- * give appropriate prefix char for -F
- */
- INLINE static char
- type_char(const struct ExAllData * slot)
- {
- char c = '\0';
- switch (slot->ed_Type) {
- case ST_ROOT:
- case ST_USERDIR:
- c = '/'; break;
- case ST_SOFTLINK:
- case ST_LINKDIR:
- c = '@'; break;
- case ST_LINKFILE:
- c = '#'; break;
- }
- return c;
- }
-
- /*
- * total
- * print total size of a directory
- */
- INLINE static void
- total(const struct ExAllData ** slots,
- int entries)
- {
- /* Normally, each entry takes a block */
- short i;
- long used_blocks = 0;
-
- for (i = 0; i < entries; i++) {
- /* Correct for small FFS files only */
- used_blocks += slots[i]->ed_Blocks;
- }
- /* Print only # of used blocks */
- FPrintf( Stdout, "total %ld\n", used_blocks + 1 >> 1);
- }
-
- /* ST_PIPEFILE (-5) -> "p" for pipes
- * ST_LINKFILE (-4) -> "h" for hard link to file
- * ST_FILE (-3) -> "-" for plain files
- * ST_ROOT 1 -> "r" for root directory
- * ST_USERDIR 2 -> "d" for ordinary directory
- * ST_SOFTLINK 3 -> "l" for soft link
- * ST_LINKDIR 4 -> "D" for hard link to dir
- */
- static char f[10] = "ph-210rdlD";
- static char r[2] = "-r";
- static char w[8] = "-D-DWwWw";
- static char x[8] = "-xTtSsSs";
- static char pchars[11];
-
- /*
- * getprot
- * get a Unix style protection bit representation
- */
- static UBYTE *
- getprot(BYTE type, LONG pbits, UWORD owner)
- {
- UBYTE *p = pchars;
-
- if (type < ST_PIPEFILE)
- type = ST_PIPEFILE;
- else if (type > ST_LINKDIR)
- type = ST_LINKDIR;
-
- type -= ST_PIPEFILE;
-
- /* Invert lowest 4 bits */
- pbits ^= 0x0f;
-
- /* Is the file owned by Mr. Nobody? */
- if (owner == 0) {
- ULONG bits = pbits & (FIBF_READ | FIBF_DELETE | FIBF_WRITE | FIBF_EXECUTE);
- pbits |= bits << FIBB_GRP_DELETE | bits << FIBB_OTR_DELETE ;
- }
-
- *p++ = f[type];
- *p++ = r[1 & (pbits >> FIBB_READ)];
- *p++ = w[7 & (pbits >> FIBB_DELETE)];
- *p++ = x[(1 & (pbits >> FIBB_EXECUTE)) +
- (4 & (pbits >> muFIBB_SET_UID - 2))];
- *p++ = r[1 & (pbits >> FIBB_GRP_READ)];
- *p++ = w[7 & (pbits >> FIBB_GRP_DELETE)];
- *p++ = x[1 & (pbits >> FIBB_GRP_EXECUTE)];
- *p++ = r[1 & (pbits >> FIBB_OTR_READ)];
- *p++ = w[7 & (pbits >> FIBB_OTR_DELETE)];
- *p++ = x[(1 & (pbits >> FIBB_OTR_EXECUTE)) +
- (2 & (pbits >> FIBB_PURE - 1)) + /* seen as 'sticky' */
- (4 & (pbits >> FIBB_SCRIPT - 2))];
- *p++ = '\0';
- return pchars;
- }
-
- static int current_year = 0;
- static UBYTE times[16];
-
- /*
- * lsdate
- * convert date into ls format
- */
- static UBYTE *
- lsdate(const struct ExAllData * slot)
- {
- int year;
- UBYTE dates[LEN_DATSTRING];
-
- #if __SASC == 0
- struct DateTime datetime =
- { { 0, 0, 0}, 0, 0, NULL, dates, times + 7};
- #else
- struct DateTime datetime =
- { { 0, 0, 0}, 0, 0, NULL, NULL, NULL};
-
- datetime.dat_StrDate = dates;
- datetime.dat_StrTime = times + 7;
- #endif
- if (!current_year) {
- (void) DateStamp(&datetime.dat_Stamp);
- (void) DateToStr(&datetime);
- current_year = 1900 + (dates[7] - '0') * 10 + dates[8] - '0';
- }
-
- datetime.dat_Stamp.ds_Days = slot->ed_Days;
- datetime.dat_Stamp.ds_Minute = slot->ed_Mins;
- datetime.dat_Stamp.ds_Tick = slot->ed_Ticks;
- (void) DateToStr(&datetime);
-
- /* if file's year is not current, print it instead of time */
- year = 1900 + (dates[7] - '0') * 10 + dates[8] - '0';
- if (year != current_year) {
- times[7] = '1';
- times[8] = '9';
- times[9] = dates[7];
- times[10] = dates[8];
- times[11] = ' ';
- }
-
- times[12] = '\0'; /* don't show seconds */
-
- /* Hack date field for printing */
- times[0] = dates[3];
- times[1] = dates[4];
- times[2] = dates[5];
- times[3] = ' ';
- if (dates[0] == '0')
- times[4] = ' ';
- else
- times[4] = dates[0];
- times[5] = dates[1];
- times[6] = ' ';
-
- return times;
- }
-
- /*
- * printlink
- * print out the soft link
- */
- static void
- printlink(const char * path,
- const struct ExAllData * slot)
- {
- static char *buffer = NULL;
- BPTR lock;
- struct FileLock *fl;
-
- if (!buffer) {
- buffer = malloc(MAXPATHLEN);
- if (!buffer) return;
- }
-
- lock = Lock(path, SHARED_LOCK);
- if (fl = (struct FileLock *)(lock << 2)) {
- if (ReadLink(fl->fl_Task, lock, slot->ed_Name, buffer, MAXPATHLEN))
- FPrintf(Stdout, " -> %s", buffer);
- UnLock(lock);
- }
- }
-
- /*
- * shortprint
- * list a directory in short format
- */
- static void
- shortprint(const struct ExAllData ** slots,
- int entries,
- struct options options)
- {
- int width;
- int rows, columns;
- int i, j;
- UBYTE * widths = malloc(entries + 1);
- UBYTE * col_widths = malloc(entries + 1);
- if (!widths || !col_widths)
- exit(RETURN_FAIL);
-
- if (options.singlecolumn)
- width = 0;
- else {
- width = getconwidth(Stdout);
- /* unknown width, but forced multicolumn */
- if (width == 0 && options.multicolumn)
- width = DEFAULTWIDTH;
- }
-
- if (width > FUDGE + 1) {
- for(i = 0; i < entries; i++) {
- widths[i] = strlen(slots[i]->ed_Name);
- if (options.kilos)
- widths[i] += 5; /* no files longer than 9999 kilos ;) */
- if (options.inode)
- widths[i] += 7; /* actually no key blocks > 4194303 */
- if (options.filetype && type_char(slots[i]))
- widths[i]++;
- }
- rows = count_rows(widths, col_widths, entries, width, FUDGE);
- columns = entries / (rows + 1) + 1;
- } else {
- rows = entries;
- columns = 1;
- }
-
- if (options.kilos)
- total(slots, entries);
-
- for (i = 0; i < rows; i ++) {
- UBYTE * col_width = col_widths;
-
- checkbreak();
-
- for (j = i; j < entries; j += rows ) {
- char filetype;
- if (options.inode)
- FPrintf(Stdout, "%6ld ", slots[j]->ed_Key);
- /* if -s is wanted */
- if (options.kilos)
- FPrintf(Stdout, "%4ld ", slots[j]->ed_Blocks + 1 >> 1);
- FPuts(Stdout, (char *)(slots[j]->ed_Name));
- if (options.filetype && (filetype = type_char(slots[j])))
- FPutC(Stdout, filetype);
-
- if (j + rows < entries) { /* tabulate */
- FWrite(Stdout,
- " "
- " ",
- /* 80 spaces, max filename lenght == 80 */
- sizeof(char), *col_width++ - widths[j] + FUDGE);
- } else {
- FPutC(Stdout, '\n');
- }
- }
- }
- free(widths);
- free(col_widths);
- }
-
- /*
- * longprint
- * list directory in long format
- */
- static void
- longprint(const char * path, const struct ExAllData ** slots,
- int entries, struct options options)
- {
- int i;
- char filetype;
-
- total(slots, entries);
-
- for (i = 0; i < entries; i++) {
- long va_list[10];
- const struct ExAllData * slot = slots[i];
-
- checkbreak();
-
- /* if -i is wanted */
- if (options.inode)
- FPrintf(Stdout, "%6ld ", slot->ed_Key);
- /* if -s is wanted */
- if (options.kilos)
- FPrintf(Stdout, "%4ld ", slot->ed_Blocks + 1 >> 1);
-
- /* normal -l listing */
- va_list[0] = (long)getprot(slot->ed_Type, slot->ed_Prot, slot->ed_OwnerUID);
- va_list[1] = 1;
- va_list[2] = (long)user(slot->ed_OwnerUID);
- if (options.group)
- va_list[3] = (long)group(slot->ed_OwnerGID);
- else
- va_list[3] = (long)"";
- va_list[4] = slot->ed_Size;
- /* This is a kludge */
- va_list[5] = (long)lsdate(slot);
- va_list[6] = (long)(options.pathname ? path : "");
- va_list[7] = (long)slot->ed_Name;
- VFPrintf(Stdout, options.group ? "%s %2ld %s %s %8ld %s %s%s" :
- "%s %2ld %s %s%8ld %s %s%s", va_list);
-
- if (options.filetype && (filetype = type_char(slot)))
- FPutC(Stdout, filetype);
- if (!options.symbolic && (slot->ed_Type == ST_SOFTLINK)) {
- printlink(path, slot);
- }
- FPutC(Stdout, '\n');
- }
- }
-
- static int
- compare_name(const struct ExAllData * p, const struct ExAllData * v)
- {
- return Stricmp(p->ed_Name, v->ed_Name);
- }
-
- static int
- compare_name_rev(const struct ExAllData * p, const struct ExAllData * v)
- {
- /* p and v reversed... */
- return Stricmp(v->ed_Name, p->ed_Name);
- }
-
- static int
- compare_time(const struct ExAllData * p, const struct ExAllData * v)
- {
- return CompareDates((struct DateStamp*)&(p->ed_Days),
- (struct DateStamp*)&(v->ed_Days));
- }
-
- static int
- compare_time_rev(const struct ExAllData * p, const struct ExAllData * v)
- {
- /* p and v reversed... */
- return CompareDates((struct DateStamp*)&(v->ed_Days),
- (struct DateStamp*)&(p->ed_Days));
- }
-
- static short
- complete(char * path)
- {
- short pathlen = strlen(path);
- /* add directory separator to path, if none */
- if (pathlen > MAXPATHLEN - MAXFILENAMELEN - 1)
- return -1;
- if (pathlen > 0 && path[pathlen - 1] != ':' && path[pathlen - 1] != '/') {
- path[pathlen++] = '/'; path[pathlen] = '\0';
- }
- return pathlen;
- }
-
- void
- doprint(char * path,
- const struct ExAllList *buffers,
- struct options options)
- {
- int entry = 0, dentry = 0, i, di;
- int entries = buffers->El_Number;
- struct ExAllData **dslots, **slots;
- short pathlen;
-
- checkbreak();
-
- if (!entries) {
- FPuts(Stdout, "Volume or directory is empty.\n");
- return;
- }
- slots = malloc((entries + 1) * sizeof(char *));
- if (!slots) {
- exit(RETURN_FAIL);
- }
-
- if ((pathlen = complete(path)) < 0) {
- /* pathname overflow - break... */
- FPrintf(Stderr, "%s: too long pathname\n", path);
- return;
- }
-
- for (; buffers; buffers = buffers->El_Next) {
- struct ExAllData *ptr;
- for (ptr = &(buffers->El_Data); ptr; ptr = ptr->ed_Next) {
- /* Add entry into table */
- dentry += (ptr->ed_Type > 0);
- slots[entry++] = ptr;
- if (entry > entries) {
- FPrintf(Stderr,
- "ls: Fatal error, entry count "
- "mismatch %ld != %ld\n", entries, entry);
- exit(RETURN_FAIL);
- }
- }
- }
- if (!options.fast && entries > 1) {
- int (*compare)(void *, void *) = NULL;
-
- if ( options.reverse ) {
- compare = options.sort_time ? (compare_time_rev) : (compare_name_rev);
- } else {
- compare = options.sort_time ? (compare_time) : (compare_name);
- }
-
- quick_sort((void **)slots, entries, compare);
- }
- dslots = malloc((dentry + 1) * sizeof(char *));
-
- if (!dslots)
- exit(RETURN_FAIL);
-
- for (i = 0, di = 0, entry = 0; i < entries; i++) {
- if (slots[i]->ed_Type > 0) {
- dslots[di++] = slots[i];
- if (!options.dir) continue;
- }
- slots[entry++] = slots[i];
- }
-
- if (entry) {
- if (options.longformat) {
- longprint(path, slots, entry, options);
- } else {
- shortprint(slots, entry, options);
- }
- }
-
- free(slots);
- if (!options.dir) {
- static int donewline = 0;
- struct ExAllList *listing;
-
- options.dir = !options.recursion;
-
- for (i = 0; i < dentry; i++) {
- /* concatenate new entry to path name */
- strncpy(path + pathlen, (char *)(dslots[i]->ed_Name),
- MAXPATHLEN - pathlen);
- /* avoid newline at top */
- if (donewline)
- FPutC(Stdout, '\n');
- if (dentry > 1 || donewline)
- FPrintf(Stderr, "%s:\n", path[0] ? path : "\"\"" );
- donewline = 1;
- listing = listdir(path, options);
- if (listing)
- doprint(path, listing, options);
- bfree(listing);
- }
- }
- free(dslots);
- }
-